package com.gopay.bis.nucc.bcdl.impl;

import com.gopay.bis.nucc.check.operation.CheckBillUtils;
import com.gopay.bis.nucc.check.vo.FileInf;
import com.gopay.bis.nucc.check.vo.NuccCheckBillBaseMessage;
import com.gopay.bis.nucc.common.util.NuccCertHelper;
import com.gopay.bis.nucc.common.util.NuccHttpClientUtil;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.domain.bank.nucc.bcdl.eft.res.NuccBcdlRes;
import com.gopay.common.domain.bank.nucc.common.base.NuccBaseMessage;
import com.gopay.common.exception.GopayException;
import com.gopay.common.util.DateUtils;
import com.gopay.common.util.SftpUtil;
import com.gopay.remote.bank.businesses.BcdlService;
import com.thoughtworks.xstream.XStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

/**
 * 网联付款申请接口
 * 
 * @NuccBcdlServiceImpl.java
 * @author zhengxue
 * @2018-1-18 下午1:52:29 www.gopay.com.cn Inc.All rights reserved.
 */
public class NuccBcdlServiceImpl implements BcdlService {
    private final static Logger logger = LoggerFactory.getLogger("BANK_LOG");

    /**
     * 连接超时时间
     */
    private static int TIME_OUT = 300000;

    private String sftpHostname;
    
    private String sftpPort;
    
    private String sftpUsername;
    
    private String sftpPassword;
    
    private String sftpLocpath;

    /**
     * 密钥明文
     */
    private String key;

    /**
     * 国付宝机构号
     */
    private String issrid;

    /**
     * 私钥路径
     */
    private String privateKeyPath;

    private List names = new ArrayList();

    private String localPath;
    
    private NuccCertHelper nuccCertHelper;

    private NuccHttpClientUtil nuccHttpClientUtil;

    public Object eft(Object object) throws GopayException {
        NuccBaseMessage message = (NuccBaseMessage) object;
        NuccBcdlRes res = null;
        try{
            message = nuccCertHelper.certEncryptBcdl(message);
            message = nuccCertHelper.sign(message);
            Map<String,String> map = new HashMap<>();
            map.put("MsgTp", message.getMsgHeader().getMsgTp());
            map.put("OriIssrId", message.getMsgHeader().getIssrId());
            String response = nuccHttpClientUtil.send(message.getPayUrl(), message.getPlainMessage()+message.getSignMessage(), map);
            res = (NuccBcdlRes) nuccCertHelper.verify(response,  NuccBcdlRes.class);
            if(res == null){
                logger.error("网联付款申请异常，返回跳转报文为空");
                throw new GopayException(ProcCodeConstants.PROC_CODE_100E6045);
            }
        }catch (GopayException e) {
            logger.error("网联付款接口异常" , e);
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6045,e);
        } catch (Exception ex) {
            logger.error("网联付款接口异常" , ex);
            throw new GopayException(ProcCodeConstants.PROC_CODE_100E6045,ex);
        }
        return res;
    }

    public Object query(Object object) throws GopayException {
        // TODO Auto-generated method stub
        return null;
    }

    public Object queryBalance(Object object) throws GopayException {
        // TODO Auto-generated method stub
        return null;
    }

    public Object packetEft(Object object) throws GopayException {
        // TODO Auto-generated method stub
        return null;
    }

    public Object queryPacket(Object object) throws GopayException {
        // TODO Auto-generated method stub
        return null;
    }

    public Map queryAcctDetail(Object object) throws GopayException {
    	Map map = new HashMap<String, Object>();
    	names.clear();
    	try{
    		  //拼接当前批次号
            StringBuilder batNoSb = new StringBuilder("B");
            String strTmp = DateUtils.date2string(DateUtils.add(new Date(),Calendar.HOUR,-3),"yyyyMMddHH");
            String dateStr = strTmp.substring(0,8);//日期
            String hourStr = strTmp.substring(8,10);//小时
            int hourInt = Integer.parseInt(hourStr)+1;
            if (hourInt<10) {
                batNoSb.append(dateStr).append("000").append(hourInt);
            } else {
                batNoSb.append(dateStr).append("00").append(hourInt);
            }
            String batNo = batNoSb.toString();
            
    	  //拼接汇总文件名称
        StringBuilder summaryFileNameSb = new StringBuilder("HZ_");
        summaryFileNameSb.append(batNoSb).append("_").append(batNoSb).append(".xml.sec");
        String summaryFileName = summaryFileNameSb.toString();
        //拼接密钥文件名称
        StringBuilder keyFileNameSb = new StringBuilder(issrid);
        keyFileNameSb.append("-").append(dateStr).append(".key");
        String keyFileName = keyFileNameSb.toString();

        //sftp路径
        StringBuilder downloadDirectorySb = new StringBuilder("/");
        downloadDirectorySb.append(issrid).append("/").append(dateStr).append("/trans/");
        String downloadDirectory = downloadDirectorySb.toString();

        int port = Integer.parseInt(sftpPort);

        //创建本地目录
        String pathDate = dateStr+"/";
        localPath = sftpLocpath+pathDate;
        File fileDate = new File(localPath);
        if (!fileDate.exists()) {
            fileDate.mkdir();
        }
        localPath = localPath + batNo + "/";
        fileDate = new File(localPath);
        if (!fileDate.exists()) {
            fileDate.mkdir();
        }

        boolean boo = true;
        // 获取秘钥文件
        boo = this.download(sftpHostname, port, downloadDirectory, keyFileName, true);
        if (!boo){
           return null;
        }
        //获取汇总文件
        boo = this.download(sftpHostname, port, downloadDirectory, summaryFileName, false);
        if (!boo){
            return null;
        }
        //解析汇总文件报文
        //读取汇总文件内容
        int index = summaryFileName.indexOf(".sec");
        String decryptFileName = summaryFileName.substring(0,index);
        String summryContent = this.readFile(localPath+decryptFileName);
        int indexPStart = summryContent.indexOf("<root>");
        String plainMsg = summryContent.substring(indexPStart);
        XStream xstream = new XStream();
        xstream.autodetectAnnotations(true);
        xstream.processAnnotations(NuccCheckBillBaseMessage.class);
        NuccCheckBillBaseMessage xmlMessage = (NuccCheckBillBaseMessage) xstream.fromXML(plainMsg);
        List<FileInf> fileInfList = xmlMessage.getDtlFileInf().getFileInfList();
        for (FileInf fileInf : fileInfList) {
            String filePth = fileInf.getFilePth();
            //解析文件路径 获取服务器地址、端口及获取路径
            int pathIndex = filePth.indexOf("/");
            String url = filePth.substring(0,pathIndex);
            String path = filePth.substring(pathIndex);
            String[] str = url.split(":");
            List<String> fileNameList = fileInf.getFileNameList().getFileName();
            for (String fileName : fileNameList) {
                //获取明细文件
                this.download(str[0], Integer.parseInt(str[1]), path, fileName, false);
            }
        }
        for (int i = 0; i < names.size(); i++) {
			logger.error("download fileName:"+names.get(i));
		}
        map.put("names", names);
        map.put("localPath", localPath);
        return map;
    } catch (Exception e) {
        logger.error("NuccQueryAcctDetail error: ", e);
        return null;
    }
    }


	/**
	 * 下载
	 * @param ip
	 * @param port
	 * @param path
	 * @param fileName
	 * @param flag 密钥文件标识 true为密钥文件
	 * @retrun
	 */
	private boolean download(String ip, int port, String path, String fileName, boolean flag) throws Exception{
	    SftpUtil sftpUtil = new SftpUtil();
	    //判断本地服务器是否存在文件
	    if (!(new File(localPath + fileName).exists())) {
	        //判断网联sftp服务器文件是否存在
	        if (sftpUtil.exist(ip, port, sftpUsername, sftpPassword, TIME_OUT, path, fileName)) {
	            logger.info("NuccQueryAcctDetail " + fileName + " exist!");
	            //下载文件
	            sftpUtil.download2(ip, port, sftpUsername, sftpPassword, TIME_OUT, path, fileName, localPath+fileName);
	            logger.info("NuccQueryAcctDetail " + fileName + " downLoad success!");
	
	            if (flag) {
	                //下载文密钥文件后，获取密钥明文并存入到该文件中
	                this.decryptKey(localPath + fileName);
	            } else {
	                //解密 获取解密后文件路径及文件名
	                String name = this.decryptFile(fileName);
	                names.add(name);
	            }
	
	            return true;
	        } else {
	            logger.error("NuccQueryAcctDetail " + fileName + " not exist!");
	            //密钥文件 汇总文件 明细文件 任一不存在返回false 结束任务
	            return false;
	        }
	    } else {
	        logger.error("NuccQueryAcctDetail local " + fileName + " 已成功下载, 不再进行sftp操作!");
	        if (flag) {
	            //如果密钥文件已经存在于本地服务器，则从中获取文件秘钥明文
	            this.decryptKey(localPath + fileName);
	        }
	        return true;
	    }
	}

	/**
	 * 获取文件内容
	 *
	 * @param path
	 * @return
	 * @throws IOException
	 */
	 private String readFile(String path) throws IOException {
	    FileReader fr = new FileReader(path);
	    BufferedReader br = new BufferedReader(fr);
	    StringBuffer sb = new StringBuffer();
	    while (br.ready()) {
	        sb.append(br.readLine());
	    }
	    return sb.toString();
	}

	/**
	 * 解密密钥
	 *
	 * @param localFile
	 * @return
	 * @throws IOException
	 */
	private void decryptKey(String localFile) throws Exception {
	    //获取内容
	    String content = readFile(localFile);
	    //拆分字符串获取证书序列号、密钥密文
	    String contents[] = content.split("\\|");
	    String certNo = contents[0];
	    String keyEncrypt = contents[1];
	    key = CheckBillUtils.decode(keyEncrypt,privateKeyPath);
	}
	
	/**
	 * 解密文件
	 * @param fileName
	 * @return
	 */
	public String decryptFile(String fileName) throws Exception{
	    //获取解密后文件全路径文件名
	    int index = fileName.indexOf(".sec");
	    String decryptFileName = fileName.substring(0,index);
	    String[] keys = key.split("\\|");
	    CheckBillUtils.decodeFile(localPath+fileName, localPath+decryptFileName, keys[1]);
	    return decryptFileName;
	}



    public NuccCertHelper getNuccCertHelper() {
        return nuccCertHelper;
    }

    public void setNuccCertHelper(NuccCertHelper nuccCertHelper) {
        this.nuccCertHelper = nuccCertHelper;
    }

    public NuccHttpClientUtil getNuccHttpClientUtil() {
        return nuccHttpClientUtil;
    }

    public void setNuccHttpClientUtil(NuccHttpClientUtil nuccHttpClientUtil) {
        this.nuccHttpClientUtil = nuccHttpClientUtil;
    }

	public String getSftpHostname() {
		return sftpHostname;
	}

	public void setSftpHostname(String sftpHostname) {
		this.sftpHostname = sftpHostname;
	}

	public String getSftpPort() {
		return sftpPort;
	}

	public void setSftpPort(String sftpPort) {
		this.sftpPort = sftpPort;
	}

	public String getSftpUsername() {
		return sftpUsername;
	}

	public void setSftpUsername(String sftpUsername) {
		this.sftpUsername = sftpUsername;
	}

	public String getSftpPassword() {
		return sftpPassword;
	}

	public void setSftpPassword(String sftpPassword) {
		this.sftpPassword = sftpPassword;
	}

	public String getSftpLocpath() {
		return sftpLocpath;
	}

	public void setSftpLocpath(String sftpLocpath) {
		this.sftpLocpath = sftpLocpath;
	}

	public String getIssrid() {
		return issrid;
	}

	public void setIssrid(String issrid) {
		this.issrid = issrid;
	}

	public String getPrivateKeyPath() {
		return privateKeyPath;
	}

	public void setPrivateKeyPath(String privateKeyPath) {
		this.privateKeyPath = privateKeyPath;
	}


}
